#include "EventLoop.h"

#include "Channel.h"
#include "CurrentThread.h"
#include "Logging.h"
#include "mutex.h"
#include "utils.h"

#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <mutex>
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <sys/types.h>
#include <unistd.h>
#include <vector>

using namespace monsoon;
namespace droneswarm
{
    //该EventLoop对象是一个线程局部变量，每个线程只能有一个EventLoop对象
    thread_local EventLoop *t_loopInThisThread = nullptr;

    //创建文件描述符的函数，通过这个函数可以创建一个唤醒的文件描述符
    int createEventfd()
    {
        /*
            eventfd是一个 Linux 系统调用，用于创建一个事件文件描述符，这个文件描述符可以用于在进程或线程之间进行事件通知。
            参数：initval：事件文件描述符的初始值。设为0表示初始值为0。
            flags：指定文件描述符的行为，可以是以下标志的组合：
            EFD_NONBLOCK：使文件描述符为非阻塞模式，即读写操作不会阻塞。
            EFD_CLOEXEC：在执行 exec 系列函数时，自动关闭该文件描述符，避免文件描述符泄露到子进程。
        */
        int evtfd = eventfd( 0, EFD_NONBLOCK | EFD_CLOEXEC );  //创建一个文件描述符
        if ( evtfd < 0 )
        {
            LOG << "Failed in eventfd" << '\n';
            abort();
        }
        return evtfd;
    }

    EventLoop::EventLoop()
        : looping_( false )
        , poller_( new Epoll() )
        , wakeupFd_( createEventfd() )
        , quit_( false )
        , eventHandling_( false )
        , callingPendingFunctors_( false )
        , threadId_( tid() )
        , pwakeupChannel_( new Channel( this, wakeupFd_ ) )
    {
        //检查是否存在多个EventLoop对象
        if ( t_loopInThisThread )
        {
            LOG << "Anther EventLoop " << t_loopInThisThread << " exists in this thread " << tid() << '\n';
        }
        else
        {
            t_loopInThisThread = this;
        }

        //设置唤醒文件描述符的事件类型
        pwakeupChannel_->set_Events( EPOLLIN | EPOLLET | EPOLLONESHOT );
        //设置唤醒文件描述符的读事件的回调函数
        pwakeupChannel_->setReadHandler( std::bind( &EventLoop::handleRead, this ) );
        //设置唤醒文件描述符的连接事件的回调函数
        pwakeupChannel_->setConnHandler( std::bind( &EventLoop::handleupdateWakeup, this ) );
        //将唤醒文件描述符的channel添加到epoll实例中
        poller_->epoll_add( pwakeupChannel_, 0 );
    }
    EventLoop::~EventLoop()
    {
        LOG << "EventLoop " << this << " of thread " << threadId_ << " closure in thread " << tid() << '\n';
        close( wakeupFd_ );
        t_loopInThisThread = nullptr;
    }

    void EventLoop::wakeup()
    {
        //唤醒的方法是往这个文件描述符中写入数据
        uint64_t one = 1;
        ssize_t n = write( wakeupFd_, ( char * )( &one ), sizeof one );
        if ( n != sizeof one )
        {
            LOG << "EventLoop::wakeup() writes " << n << " bytes instead of 8" << '\n';
        }
    }
    void EventLoop::handleRead()
    {
        //处理读事件
        uint64_t one = 1;
        ssize_t n = read( wakeupFd_, &one, sizeof one );
        if ( n != sizeof one )
        {
            LOG << "EventLoop::handleRead() reads " << n << " bytes instead of 8" << '\n';
        }
        //重新设置唤醒文件描述符的事件类型
        /*
        EPOLLONESHOT 是 Linux 中的一个标志，用于 epoll 事件通知机制。
        当你在 epoll 中注册一个文件描述符（例如，一个 socket）时，你可以设置 EPOLLONESHOT
        标志。这意味着，一旦这个文件描述符上的事件被触发，
        epoll将不再报告任何关于这个文件描述符的事件，除非你再次修改它的事件掩码。
        */
        pwakeupChannel_->set_Events( EPOLLIN | EPOLLET | EPOLLONESHOT );
    }
    void EventLoop::doPendingFunctors()
    {
        std::vector< Functor > functors;
        callingPendingFunctors_ = true;
        {
            std::lock_guard< std::mutex > lock( mutex_ );
            functors.swap( pendingFunctors_ );  //容器内容互换。
        }
        //执行回调函数
        for ( size_t i = 0; i < functors.size(); ++i )
        {
            functors[ i ]();
        }
        callingPendingFunctors_ = false;
    }
    
    //这个名称有误导啊
    void EventLoop::handleupdateWakeup()
    {
        //处理连接事件
        // std::cout << "handleupdateWakeup" << std::endl;
        updatePoller( pwakeupChannel_, 0 );
    }
    //开启事件循环
    void EventLoop::loop()
    {
        CondPanic( !looping_, "EventLoop is already looping!" );
        CondPanic( isInLoopThread(), "EventLoop is not in current thread!" );
        looping_ = true;
        quit_ = false;
        LOG << "EventLoop " << this << " start looping" << '\n';
        //创建一个存放就绪事件的Channel的容器
        std::vector< SP_Channel > ret;
        // std::cout << "Loop 循环执行中！" << std::endl;
        while ( !quit_ )
        {
            // 1、清空容器
            ret.clear();
            // 2、用容器接收活跃的事件所属的Channel
            ret = poller_->poll();  // poll()函数就是返回已就绪的事件的容器
            eventHandling_ = true;
            // 3、遍历容器，处理活跃的事件
            for ( auto &it : ret )
            {
                //这里是处理事件，首先会将自己需要执行的函数绑定到channel中。
                it->HandleEvent();
            }
            eventHandling_ = false;
            // 4、执行回调函数
            // doPendingFunctors();  //在这个回调函数中执行设置channel事件属性的函数。
            poller_->handleExpired();  //它每次在这里都会每次循环时都会处理到期事件
        }
        looping_ = false;
    }

    //退出事件循环
    void EventLoop::quit()
    {
        quit_ = true;
        //如果当前线程不是事件循环的线程，则唤醒事件循环的线程
        if ( !isInLoopThread() )
        {
            wakeup();
        }
    }
    //在当前事件循环线程执行cb
    void EventLoop::runInLoop( Functor &&cb )
    {
        //如果当前线程是事件循环的线程，则直接执行cb
        if ( isInLoopThread() )
        {
            cb();
        }
        else
        {
            //如果当前线程不是事件循环的线程，则将cb放入队列中，唤醒事件循环的线程执行cb
            queueInLoop( std::move( cb ) );
        }
    }
    //将cb放入队列中，唤醒IO线程执行
    //这个函数主要是用来将event添加到pendingFunctors_中
    void EventLoop::queueInLoop( Functor &&cb )
    {
        {
            std::lock_guard< std::mutex > lock( mutex_ );
            // emplace_back是 C++11 引入的 STL 容器方法，允许直接在容器末尾构造对象。
            // emplace_back 与 push_back 的区别在于，push_back 会调用拷贝构造函数，而 emplace_back 则是直接在容器上构造对象。
            pendingFunctors_.emplace_back( std::move( cb ) );
        }
        //如果当前线程不是事件循环的线程，则唤醒事件循环的线程
        if ( !isInLoopThread() || callingPendingFunctors_ )
        {
            wakeup();
        }
    }
}  // namespace droneswarm